#define NODEID 0xFF
#define SUBSLOTTICS 170
#define NODESPERSLOT 4
#define NODESTOTAL 16
// wenn ein Chronloc Paket fehlt, muss es auf Null gesetzt werden (sieht man z.B. bei Restart)
#define SLOTSPERTX (NODESTOTAL / NODESPERSLOT)
#define NODESLOT (NODEID / NODESPERSLOT)

/*
Messungen Stromverbrauch Release 3. Mai 2013
auf "Mini" Board
Gemessen mit Oszi Parallel zu BAT85
Dann mit Widerstand verglichen:
Vollbetrieb 150 Ohm
Ohne Radio ca 470 Ohm 
PM2 150 kOhm 
Bei 3 Volt Vcc
-> 20/6 mA, 20 uA
OSZI 5 mV/DIV
*/

// Chonos lässt das Paket Nummer Null aus !!!
#define BASEADR 0x11223300
#define TXSFDLEAD (29+25)
#define MINCHRONPERIOD 850

#include <stdio.h>
#include <string.h>

#include "hal.h"
#include "RF04EB.h"
#include "cc_radio.h"
//#include "myglobals.h"
//#include "us.h"
#include "trx_v1.h"
#include "mycc1110radio.h"

#define P_BEACONS 1
#define P_SNIFF 2
#define P_ACCESS 3
#define P_REPEAT 4


extern volatile BITVAR rx_newSFD; // neues Paket wird empfangen 
extern UINT8 rx_len; // Laenge des neuen Pakets
extern volatile mytime32 rx_sfdtime;
extern volatile UINT8 tx_idle; // UART TX
volatile mytime32 sfdtime_ms;
volatile mytime32 lastSyncTime;
volatile mytime32 timeUS;
//volatile mytime32 test;

UINT16 timediff; // Hilfsvariable für Zeit seit letzem RFD
UINT16 senddelaytics; 
UINT8 theRxBuf[256];
UINT8 theTxBuf[64];
UINT8 theRssiBuf[SLOTSPERTX];

extern volatile UINT8 timer3Interrupt;
extern volatile UINT8 sio_rx_has_line; 
extern UINT8 theSioInpLine[SIO_INBUFLEN];

UINT8 RXcallBackFunction(void); 
void dumpPacket(void);
void prhex(unsigned char x);

UINT8 PROGMODE;
sim_frame *simptr, *assocptr, *simtx;
UINT32 myadr, accadr;
UINT8 tempu8;
UINT8 balt, bneu;
char c;
UINT8 timertic; // fuer sleep mode
UINT8 sleep_mod; 
UINT8 breaks_in_mod;
// forward
void go_pm2(void);

void main(void)
{

   static UINT8 i;
   static volatile UINT16 i16;
   static UINT8 errcnt;
   long blinx;
  UINT8 plen;
  UINT16 lasttest, starttest, test;
  UINT32 q;
  UINT8 theAssocCmd[40] = {  0x12, 0x33 , 0x70 , 0x10 , 0x01 , 0x78 , 0x56 , 0x34 , 0x10 , 0x03 \
			 , 0x21 , 0x49 , 0x81 , 0x90 , 0xEF , 0xBE , 0xAD , 0xDE , 0x00 };
  UINT8 the2ndCmd[40] = { 0x0F , 0x33 , 0x70 , 0x10 , 0x01 , 0x78 , 0x56 , 0x34 , 0x10 , 0x02 \
			 , 0x20 , 0xA0 , 0x81 , 0x23 , 0x20 , 0x00};
  UINT8 rxmodulo; // die letzten Bits der laufenden Nummer Chronos Paket
  UINT8 chronlfd; // laufende Nummer des Chronos-Pakets
//  myadr = 0x10345678;
  myadr = BASEADR | NODEID;
  accadr = BASEADR | 0xff;
  if (NODEID == 0xFF) 
    PROGMODE = P_ACCESS;
  else {
    PROGMODE =  P_REPEAT;
    }
//  PROGMODE =  P_SNIFF;
  simptr = (void*) theRxBuf;
  SET_32KHZ_CLOCK_SOURCE(CRYSTAL);
  SET_MAIN_CLOCK_SOURCE(CRYSTAL);
  CLKCON &= 0x00;    
  
  
/*   CLKCON &= ~0x80;  // Auf 23 Khz Oszillator auf Quartz umshalten (falls es einen gibt ... )
   SET_MAIN_CLOCK_SOURCE(CRYSTAL); // ca. 250 usec
   SLEEP |= 0x84;    // Kalibrierung und Beide 32M oszis an ist aus ... 	   
*/ 
 INIT_GLED();
 //  INIT_YLED();
 SET_GLED();

 //   CLR_YLED();
  
   init_uart(SIO_BAUD);
       mydelayms(10);
//   IO_PER_LOC_UART0_AT_PORT1_PIN2345();    //set UART0 to P1_2-5
//   IO_PER_LOC_SPI0_AT_PORT1_PIN2345();     //set SPI0 to P1_2-5
//   IO_PER_LOC_UART1_AT_PORT1_PIN4567();    //set UART1 to P1_4-7

   INT_GLOBAL_ENABLE(INT_ON);
   c = U0DBUF; // toggle receiver
//   U0DBUF = '*'; // toggle xmit
//putchar('*');
   if (PROGMODE == P_ACCESS) printf("\n \nCHRONLOC ACCESSPOINT\n");
   else printf("\n \nCHRONLOC NODE\n");
   printf("Hello World!\n");
   printf("BUILT %s %s \n" , __TIME__ , __DATE__);
   printf("NODEID %d \n" , NODEID);
 /*  printf("I@CH %d\n" , DEFAULT_CHAN);
   for (i=0;i<2; i++) printf("Hallo %d\n" , i);
   */
   {

    lastSyncTime.t32 = currtime32();
    mydelayms(50);
    lastSyncTime.t32 = currtime32() - lastSyncTime.t32;
    timeUS.t32 = lastSyncTime.t32 * 30 + (lastSyncTime.t32 >> 1);
 /*   putchar('a');
    putchar('b');
    putchar('c');*/
    printf("TIME 50000 usec: %ld \n", timeUS.t32);
    }
    
    mydelayms(300);
   INT_GLOBAL_ENABLE(INT_OFF);   
    
   // Sleep Timer auf 15 Bit einstellen
   WOREVT1 = 0x80;
   WOREVT0 = 0x00;
   mydelayus(100);
   WORCTRL = 0x74;
   mydelayus(100);
   
    lasttest = currtime16();
    starttest = lasttest;
// Timer check	
    for(q = 0; q < 500000; q++) {
      test = currtime16();
      if (test != lasttest) {
      	if (test != lasttest+1) {
		break;
		}
      	lasttest = test;
	}
//      mydelayus(20);
    }
       INT_GLOBAL_ENABLE(INT_ON); 
     		printf("\nINF caught! cnt %lu curr %u last %u start %u \n" , q, test, lasttest, starttest);


// fuer US-Trigger
   
//   us_Init();
//   rf_Timer3Init(TIMER3PERIODUS);          //set timer 3 to timer3Period usec
 
//   INT_SETFLAG(INUM_T3, INT_CLR);
//   INT_ENABLE(INUM_T3, INT_ON);

blinx = 0; 
initcc1110();
if ( PROGMODE ==  P_REPEAT) setRxTIO( 6000);
else setRxTIO(100000);

senddelaytics = (NODEID % NODESPERSLOT+1) * SUBSLOTTICS - TXSFDLEAD;
printf("INFO Senddelaytics %d\n" , senddelaytics);
simtx = (void*) theTxBuf;
for (i=0;i<NODESPERSLOT;i++) theRssiBuf[i] = 0x80; // 0x80 bedeutet "kein Wert Empfangen"

sleep_mod = 1; timertic = 0; breaks_in_mod = 0; errcnt = 0;// kurze Schlafphase

//i=0;
if (PROGMODE == P_REPEAT) for(;;) {

  GLED = !GLED; // Neues Board invertiert GLED
//  GLED = 1;
  RFST = SRX;
  timediff = currtime16();
  plen = readcc1110Packet(theRxBuf);
  RFST = SIDLE; // strom sparen
  timediff = currtime16() - timediff;
  timediff &= 0x7fff;
//  printf("%d\n", timediff);
  if ((plen==18) && ((simptr->qadr & 0xffffff00) != (BASEADR & 0xffffff00))) { // keine "Eigenen" Pakete bearbeiten
    // Paket empfangen
     sleep_mod = 1; timertic = 0; breaks_in_mod = 0; errcnt = 0; // kurze Schlafphase
//    mydelayms(5);
// Gesamtperiode > 850 (- 20 oder so Abstand) Uhr PAket ca 120
// 6 *120 = 720, also 5 Repeaterpakete pro Slot oder 4 im 150er Raster
// Repeaterpaket: QADR ZADR Uhr lfd Uhr RSSI
    chronlfd = simptr->lfd;
    rxmodulo = chronlfd & (SLOTSPERTX-1); // Slotspertx ist Zweierpotenz
    theRssiBuf[rxmodulo] = theRxBuf[16]; // der RSSI Wert
    
    if (rxmodulo == NODESLOT) { // nicht bei jeder Chronos Zeile senden ...
      
/*      theTxBuf[0] = 3;
      theTxBuf[1] = 0x22;
      theTxBuf[2] = 0x33; 
      theTxBuf[3] = 0x44; */
  
      simtx->cc_len = 10+SLOTSPERTX;
      simtx->qadr = myadr;
      simtx->zadr = accadr;
      theTxBuf[9] = NODEID;
      theTxBuf[10] = chronlfd-SLOTSPERTX+1;      
      // es folgen "SLOTSPERTX" RSSI werte
      for(i = 0; i<SLOTSPERTX;i++) {// beginnen mit dem aeltesten
	tempu8 = (rxmodulo+1+i) & (SLOTSPERTX-1);
	theTxBuf[11+i] = theRssiBuf[tempu8]; // schwierige Zeile ..
	}
      
      do {
	timediff = (currtime16() - rx_sfdtime.t16 ) & 0x7fff;
	} while (timediff < senddelaytics);

//    CLR_GLED();    
      plen = sendcc1110Packet(&theTxBuf[1], theTxBuf[0], 0);
//    SET_GLED();
// RSSI Werte löschen
      for (i=0;i<NODESPERSLOT;i++) theRssiBuf[i] = 0x80; // 0x80 bedeutet "kein Wert Empfangen"
//
      } // rxmodulo == NODESLOT

      do { // noch etwas warten bevor RX aktiv wird
	timediff = (currtime16() - rx_sfdtime.t16 ) & 0x7fff;
	} while (timediff < (MINCHRONPERIOD - TXSFDLEAD));
    } // if plen == 18 && ADR also UHR Paket empfangen
  else if (plen == 0) { //Ungueltiges Paket oder IDLE, meist nach ca 150 msec
    // also für 1 sec schlafe
    // spaeter auch fuer 10 sec schlafen
    // aber erst 2 sec weiterhorchen
    if (errcnt < 50) errcnt++;
    else go_pm2();
    }
  
  if (sio_rx_has_line) {
    printf("%s\n" , theSioInpLine);
    sio_rx_has_line = 0;
    }
  }
  
if (PROGMODE == P_BEACONS) for(;;) {

  theTxBuf[0] = 3;
  theTxBuf[1] = 0x22;
  theTxBuf[2] = 0x33; 
  theTxBuf[3] = 0x44; 
  
  plen = sendcc1110Packet(&theTxBuf[1], theTxBuf[0], 1);
//  if (i>=100) i=0;
  printf(" plen: %d\n", plen);
  mydelayms(50);
  blinx++;
  if (blinx ==  10) {CLR_GLED(); blinx = 0;};
  if (blinx ==  5) SET_GLED();
  } // for

if (PROGMODE == P_SNIFF) for(;;) {

  plen = readcc1110Packet(theRxBuf);
  printf("line %d ", plen); 
  for (i=0;i< plen;i++) prhex(theRxBuf[i]);
  printf("\n");

 if (sio_rx_has_line) {
    printf("%s\n" , theSioInpLine);
    sio_rx_has_line = 0;
  }
 } // for (Progmode == sniff)
 
if (PROGMODE == P_ACCESS) for(;;) {
  GLED = !GLED;
  plen = readcc1110Packet(theRxBuf);
  if (plen == 0) printf("TIO\n");
  if (plen>0) {
    timediff = rx_sfdtime.t16 - lastSyncTime.t16;
    timediff &= 0x7fff;
    lastSyncTime.t16= rx_sfdtime.t16;
    if (plen==22) 
      if (simptr->zadr == -1) {
	assocptr = (void*) theAssocCmd;
	assocptr->zadr = simptr->qadr;
	assocptr->qadr = myadr;
	assocptr->magic = simptr->magic;
	mydelayms(3);
	sendcc1110Packet(&theAssocCmd[1], theAssocCmd[0], 1);
	printf("ASSOC1 %08lx \n", simptr->qadr);
//	for(i = 0;i< 19;i++) prhex(theAssocCmd[i]); printf("\n");
	}
    if (plen==23) 
      if (simptr->zadr == -1) {
	assocptr = (void*) the2ndCmd;
	assocptr->zadr = simptr->qadr;
	assocptr->qadr = myadr;
	assocptr->magic = simptr->magic;
	mydelayms(3);
	sendcc1110Packet(&the2ndCmd[1], the2ndCmd[0], 1);
	printf("ASSOC2 %08lx \n", simptr->qadr);
//	for(i = 0;i< 20;i++) prhex(the2ndCmd[i]); printf("\n");
	}
     if ((plen==18) && ((simptr->qadr & 0xffffff00) != BASEADR)) {
        printf("CHRON %u ", timediff); 
	for(i = 0;i< 18;i++) prhex(theRxBuf[i]); printf("\n");	
	}
     if ((simptr->qadr & 0xffffff00) == BASEADR) { // repeater
        printf("REP %u ", timediff); 
	for(i = 0;i< plen;i++) prhex(theRxBuf[i]); printf("\n");	
	}
   } // plen > 0
   if (sio_rx_has_line) {
    printf("%s\n" , theSioInpLine);
    sio_rx_has_line = 0;
    }
  } // for PACCESS
} // main

UINT8 RXcallBackFunction(void)
{
//zero value indicates that the contents of both memory blocks are equal.
if (rx_len > 0)
  {
    sfdtime_ms.t32 = rx_sfdtime.t32;
    sfdtime_ms.t32 *= 100L;
    sfdtime_ms.t32 >>= 7;
    sfdtime_ms.t32 *= 100L;
    sfdtime_ms.t32 >>= 8;
    return(TRUE);
  }
return (FALSE);
}

void prhex(unsigned char x)
{
//printf("0x");
putchar( (x >> 4) >= 10 ? (x >> 4) + 0x41-10 : (x >> 4) | 0x30 );
putchar( (x & 0x0f) >= 10 ? (x &0x0f) + 0x41-10 : (x & 0x0f) |  0x30 );
putchar(' ');
}

void dumpPacket(void)
{
BYTE myRssi, myLqi, i;

UINT8 *p = theRxBuf;

if (0 == 1) {
  printf("RX "); 
  putbyte((rx_len+3) | 0x80); // binaermodus laengentoken
  putbyte(rx_len); putbyte(theRxBuf[rx_len]); putbyte(theRxBuf[rx_len+1]); 
  for(i=0; i<(rx_len); i++) putbyte(*p++);
  }
else {
  
  printf("RX t%ld l%d ", sfdtime_ms.t32, rx_len);
  for(i=0; i<(rx_len); i++) prhex(*p++);
  myRssi = *p++;
  myLqi = *p++;
  putchar('r');prhex(myRssi);
  putchar('q');prhex(myLqi);
  }
  printf("\n");

   // Last two bytes contain RSSI level and Correlation value in addition to CRC OK.
   // Checking that the CRC value is OK
//   sfdtime = (void*) &ReceiveData.sfdt0;
//   printf("SFD %ld Delta %ld ", *sfdtime, *sfdtime - lastsfd);
//   if(myLqi & 0x80) printf("crc OK \n");
//   else printf("crc *ERR* \n");


}



/******************************************************************************
Interrupt for TIMER3PERIODUS (value set in global.h) timer 3

3 * 600 us = 1.8 ms => this is the time we have to wait after a wake up
until the us signal is sent.
******************************************************************************/

void T3_IRQ(void) __interrupt(T3_VECTOR) {
  
     INT_SETFLAG(INUM_T3, INT_CLR);
     timer3Interrupt++;
    // after 3 * TIMER3PERIODUS stop the timer
     if(timer3Interrupt >=3) 
     {
        TIMER3_RUN(FALSE);  // stop timer3
     }
}

/*
******************************************************************************
Interrupt for wake up from sleep timer
***************************************************************************** */

// #pragma vector=ST_VECTOR
void powermodes_ST_IRQ(void) __interrupt (ST_VECTOR) {
   INT_SETFLAG(INUM_ST, INT_CLR);
//   timertic++;
}

void go_pm2(void) {
       while (!tx_idle) ; // UART noch ausschreiben
       mydelayus(200);	 // noch 2 Zeichen rauslassen
       INT_GLOBAL_ENABLE(INT_OFF);
       GLED= 0;
       
       // Timer Interrupt erlauben
// alle anderen Interrupts aus
        IEN0 = 0; IEN1=0; IEN2 = 0;
//	INT_GLOBAL_ENABLE(FALSE);
// alle IRQs löschen ausser timer
//	if ((!LED_NOBLINK) && USELED) { CLR_GLED(); CLR_YLED(); };
	P0SEL=0;P0DIR=0; P0INP=0;
	P1SEL=0;P1DIR=0; P1INP=0;
	P2SEL=0;P2DIR=0; P2INP=0;
	U0CSR=0; // UART aus
	U1CSR=0; // UART aus       
       
       WORCTRL = 0x74;
       mydelayus(100);
       WORIRQ = 0;
       WORIRQ = 0x10;           
       
	INT_SETFLAG(INUM_ST, INT_CLR);
	INT_ENABLE(INUM_ST, INT_ON);
	INT_GLOBAL_ENABLE(TRUE);	      
// Alignment of entering PM{0 – 2} to a positive edge on the 32 kHz clock source
// inner sleep loop
for(;;) {	
       
       bneu  = (WORTIME0 & 0x01);
       balt = bneu;
       while( balt | !bneu) {
	     balt = bneu;
             bneu  = (WORTIME0 & 0x01);
	     };	// Wait until a positive 32 kHz edge 
         SLEEP |= 2; // Sleep Mode is PM2
         PCON |= 0x01;	// Enter PM{0 – 2}	
// hier noch NOPS
       _asm
        nop
  	nop
  	nop
      _endasm;
      timertic++;
      if (timertic >= sleep_mod) { // Dynamische Zeitverlaengerung
	timertic =0;
	breaks_in_mod++;
	if ((sleep_mod==2) && (breaks_in_mod >=10)) {
	    sleep_mod=5;
	    breaks_in_mod=0;
	  }
	if ((sleep_mod==1) && (breaks_in_mod >=10)) {
	    sleep_mod=2;
	    breaks_in_mod=0;
	  }
	break;	
	} // Modulo Event
      } // inner sleep loop
      
       INIT_GLED();
 //  INIT_YLED();
  //     SET_GLED();
       WORIRQ = 0;
       GLED = 0; // wird bei rx auf 1 gesetzt
	INT_ENABLE(INUM_ST, INT_OFF);
	INT_SETFLAG(INUM_ST, INT_CLR);
       INT_GLOBAL_ENABLE(INT_ON);   
       SET_MAIN_CLOCK_SOURCE(CRYSTAL);
	CLKCON &= 0x00;    
       init_uart(SIO_BAUD);
        c = U0DBUF; // toggle receiver
       initcc1110();
//       printf("BrkMod %d\n" , breaks_in_mod);
}
